/* 
 * 	This file is part of VPN Monitor Project 7..

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package monitor.gui;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.util.EnumMap;
import java.util.LinkedList;

import javax.swing.JButton;
import javax.swing.JColorChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSpinner;
import javax.swing.SpinnerNumberModel;
import javax.swing.UIManager;
import javax.swing.colorchooser.AbstractColorChooserPanel;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import monitor.gui.GuiControl.SettingsEnum;
import monitor.interfaces.EnumTypes.Types;

@SuppressWarnings("serial")
class Settings extends JPanel implements ColorObserver{
	private final JLabel[] labels;
	private EnumMap<SettingsEnum, Double> logLevel = null;
	private JLabel timeSpan = null;
	private GuiControl control = null;
	
	Settings(GuiControl control, EnumMap<SettingsEnum, String> settings){
		this.control = control;
		control.addColorObserver(this);
		
		logLevel = new EnumMap<SettingsEnum, Double>(SettingsEnum.class);
		LinkedList<JLabel> labels = new LinkedList<JLabel>();
		createPanel(labels, settings);
		this.labels = new JLabel[labels.size()];
		labels.toArray(this.labels);
		for(JLabel label : labels)
			label.setForeground(SettingsEnum.color(settings.get(SettingsEnum.COLOR_FOREGROUND)));
	}
	
	EnumMap<SettingsEnum, Double> getLogLevels(){
		EnumMap<SettingsEnum, Double> logLevel = new EnumMap<SettingsEnum, Double>(this.logLevel);
		logLevel.remove(SettingsEnum.SAMPLE_DIAGRAM);
		for(SettingsEnum se : SettingsEnum.timer())
			logLevel.remove(se);
		return logLevel;
	}
	
	EnumMap<SettingsEnum, Double> getTimerLevels(){
		EnumMap<SettingsEnum, Double> timerLevel = new EnumMap<SettingsEnum, Double>(SettingsEnum.class);
		for(SettingsEnum se : SettingsEnum.timer())
			timerLevel.put(se, logLevel.get(se));
		return timerLevel;
	}
	
	int getDiagramSample(){
		return this.logLevel.get(SettingsEnum.SAMPLE_DIAGRAM).intValue();
	}
	
	@Override
	public void colorUpdated(SettingsEnum type, Color color) {
		if(type == SettingsEnum.COLOR_FOREGROUND)
			for(JLabel label : labels)
				label.setForeground(color);
		else if(type == SettingsEnum.COLOR_BACKGROUND)
			this.setBackground(color);
	}
	
	private void setTimespan(double timer, int diagram){
		final String string = "Timespan: ";
		final int day = 60*60*24, hour = 60*60, min = 60;
		StringBuilder time = new StringBuilder();
		int value = (int) (Diagram.DIAGRAM_POINTS * timer * diagram), tmp;
		if((tmp = value/day) > 0){
			time.append(tmp+"d ");
			value-=tmp*day;
		}
		if((tmp = value/hour) > 0){
			time.append(tmp+"h ");
			value-=tmp*hour;
		}
		if((tmp = value/min) > 0){
			time.append(tmp+"min ");
			value-=tmp*min;
		}
		if(value > 0)
			time.append(tmp+"s ");
		else
			time.delete(time.length()-1, time.length()+1);
		timeSpan.setText(string+time.toString());
	}
	
	private void createPanel(LinkedList<JLabel> labels, EnumMap<SettingsEnum, String>  settings){	
		this.setLayout(new GridBagLayout());
		GridBagConstraints c = new GridBagConstraints();
		this.setBackground(SettingsEnum.color(settings.get(SettingsEnum.COLOR_BACKGROUND)));
		c.anchor = GridBagConstraints.NORTHWEST;
		
		c.gridy = c.gridx = 0;
		this.add(createLogSettings(labels, settings), c);
		
		c.gridy = 0; c.gridx = 1; c.insets = new Insets(0, 10, 0, 0);
		this.add(createTimerSettings(labels, settings), c);
		
		c.gridy = 1; c.gridx = 0; c.insets = new Insets(10, 0, 0, 0);
		this.add(createColorSettings(labels, settings), c);	
		
		c.gridx = c.gridy = 1; c.insets = new Insets(10, 10, 0, 0);
		this.add(createDiagramSettings(labels, settings), c);
		
		c.gridy++; c.gridx=0; c.gridwidth=2;
		c.anchor = GridBagConstraints.CENTER;
		JButton update = new JButton("Update");
		update.setName("update");
		update.addActionListener(control);
		this.add(update, c);
	}
	
	private JPanel createLogSettings(LinkedList<JLabel> labels, EnumMap<SettingsEnum, String>  settings){
		JPanel panel = new JPanel(new GridBagLayout());
		GridBagConstraints c = new GridBagConstraints();
		panel.setOpaque(false);
		
		c.gridx = c.gridy = 0; c.gridwidth = 3;
		c.anchor = GridBagConstraints.NORTH;
		JLabel title = new JLabel("Log");
		labels.add(title);
		panel.add(title, c);
		
		c.gridy++; c.gridwidth = 1;
		c.anchor = GridBagConstraints.EAST;		
		for(Types type : Types.values()){
			final SettingsEnum logEnum = SettingsEnum.valueOf(("LOG_"+type).toUpperCase());
			Double value = Double.parseDouble(settings.get(logEnum));
			logLevel.put(logEnum, value);
			
			JLabel label = new JLabel(type.toString()+" ");
			panel.add(label, c);
			labels.add(label);
			c.gridx++;
			
			SpinnerNumberModel model = new SpinnerNumberModel(value, (Double)0.0, (Double)1e6, (Double)100.0);
			model.addChangeListener(new ChangeListener(){
				@Override
				public void stateChanged(ChangeEvent ce) {
					logLevel.put(logEnum, new Double(((SpinnerNumberModel) ce.getSource()).getNumber().doubleValue()));
				}
			});
			JSpinner spinner = new JSpinner(model);
			spinner.addMouseWheelListener(createWheelListener(spinner));
			Dimension dimension = spinner.getPreferredSize();
			dimension.width-=35;
			spinner.setPreferredSize(dimension);
			panel.add(spinner, c);
			c.gridx++;
			
			JLabel suffix = new JLabel("MB");
			panel.add(suffix, c);
			labels.add(suffix);
			c.gridy++; c.gridx-=2;
		}
		return panel;
	}
	
	private JPanel createColorSettings(LinkedList<JLabel> labels, EnumMap<SettingsEnum, String> settings){
		SettingsEnum[] names = SettingsEnum.changeable();
		
		JPanel panel = new JPanel(new GridBagLayout());
		GridBagConstraints c = new GridBagConstraints();
		panel.setOpaque(false);
		
		c.gridx = c.gridy = 0; c.gridwidth = 4;
		c.anchor = GridBagConstraints.NORTH;
		JLabel title = new JLabel("Color");
		labels.add(title);
		panel.add(title, c);
		
		c.gridy++; c.gridwidth = 1;
		c.anchor = GridBagConstraints.EAST;	
		for(int i=0;i<names.length;i++){
			JLabel label = new JLabel(names[i].toString()+" ");
			panel.add(label, c);
			labels.add(label);
			c.gridx++; c.insets = new Insets(0, 0, 0, 0);
			
			panel.add(new ColorButton(names[i], SettingsEnum.color(settings.get(names[i]))), c);
			c.gridx--; c.gridy++;
			if(i>=1){
				if(i==1){
					c.gridy-=2;
					c.gridx+=2;
				}
				c.insets = new Insets(0, 10, 0, 0);
			}
		}
		return panel;
	}
	
	private JPanel createTimerSettings(LinkedList<JLabel> labels, EnumMap<SettingsEnum, String> settings){
		JPanel panel = new JPanel(new GridBagLayout());
		GridBagConstraints c = new GridBagConstraints();
		panel.setOpaque(false);
		
		c.gridx = c.gridy = 0; c.gridwidth = 3;
		c.anchor = GridBagConstraints.NORTH;
		JLabel title = new JLabel("Timer");
		labels.add(title);
		panel.add(title, c);
		
		c.gridy++; c.gridwidth = 1;
		c.anchor = GridBagConstraints.EAST;	
		for(final SettingsEnum type : SettingsEnum.timer()){
			JLabel label = new JLabel(type.toString()+" ");
			panel.add(label, c);
			labels.add(label);
			c.gridx++;

			Double value = Double.parseDouble(settings.get(type));
			logLevel.put(type, value);
			SpinnerNumberModel model = new SpinnerNumberModel(value, (Double)0.1, (Double)120.0, (Double)0.1);
			model.addChangeListener(new ChangeListener() {

				@Override
				public void stateChanged(ChangeEvent ce) {
					logLevel.put(type, ((SpinnerNumberModel) ce.getSource()).getNumber().doubleValue());
					if(type == SettingsEnum.TIMER_TRAFFIC)
						setTimespan(logLevel.get(type), logLevel.get(SettingsEnum.SAMPLE_DIAGRAM).intValue());
				}

			});
			
			JSpinner spinner = new JSpinner(model);
			spinner.addMouseWheelListener(createWheelListener(spinner));
			panel.add(spinner, c);
			c.gridx++;
			
			JLabel suffix = new JLabel("s");
			panel.add(suffix, c);
			labels.add(suffix);
			c.gridx-=2; c.gridy++;
		}
		return panel;
	}
	
	private JPanel createDiagramSettings(LinkedList<JLabel> labels, EnumMap<SettingsEnum, String>  settings){
		JPanel panel = new JPanel(new GridBagLayout());
		GridBagConstraints c = new GridBagConstraints();
		panel.setOpaque(false);
		
		c.gridx = c.gridy = 0; c.gridwidth = 2;
		JLabel title = new JLabel("Diagram");
		labels.add(title);
		panel.add(title, c);
		
		c.gridy++; c.anchor = GridBagConstraints.WEST;
		timeSpan = new JLabel("Timespan:");
		labels.add(timeSpan);
		panel.add(timeSpan, c);
		
		c.gridy++; c.gridwidth = 1;
		JLabel diagramLabel = new JLabel("Sample size");
		labels.add(diagramLabel);
		panel.add(diagramLabel, c);
		
		c.gridx++;
		Double value = Double.parseDouble(settings.get(SettingsEnum.SAMPLE_DIAGRAM));
		logLevel.put(SettingsEnum.SAMPLE_DIAGRAM, value);
		SpinnerNumberModel model = new SpinnerNumberModel(value.intValue(), 1, 100, 1);
		model.addChangeListener(new ChangeListener(){

			@Override
			public void stateChanged(ChangeEvent ce) {
				logLevel.put(SettingsEnum.SAMPLE_DIAGRAM, ((SpinnerNumberModel) ce.getSource()).getNumber().doubleValue());
				setTimespan(logLevel.get(SettingsEnum.TIMER_TRAFFIC), logLevel.get(SettingsEnum.SAMPLE_DIAGRAM).intValue());
			}
			
		});
		JSpinner diagramSpinner = new JSpinner(model);
		diagramSpinner.addMouseWheelListener(createWheelListener(diagramSpinner));
		panel.add(diagramSpinner, c);
		
		setTimespan(logLevel.get(SettingsEnum.TIMER_TRAFFIC), logLevel.get(SettingsEnum.SAMPLE_DIAGRAM).intValue());
		
		return panel;
	}
	
 	private MouseWheelListener createWheelListener(final JSpinner spinner){
		MouseWheelListener listener = new MouseWheelListener(){
			
			@Override
			public void mouseWheelMoved(MouseWheelEvent mwe) {
				if (mwe.getScrollType() != MouseWheelEvent.WHEEL_UNIT_SCROLL)
					return;
				if (spinner.getValue() instanceof Double) {
					double value = (Double) spinner.getValue(), tmp;
					value -= mwe.getUnitsToScroll();

					if (value < (tmp = (Double) ((SpinnerNumberModel) spinner.getModel()).getMinimum()))
						value = tmp;
					else if (value > (tmp = (Double) ((SpinnerNumberModel) spinner.getModel()).getMaximum()))
						value = tmp;

					spinner.setValue(value);
				} else if(spinner.getValue() instanceof Integer){
					int value = (Integer) spinner.getValue(), tmp;
					value -= mwe.getUnitsToScroll();

					if (value < (tmp = (Integer) ((SpinnerNumberModel) spinner.getModel()).getMinimum()))
						value = tmp;
					else if (value > (tmp = (Integer) ((SpinnerNumberModel) spinner.getModel()).getMaximum()))
						value = tmp;

					spinner.setValue(value);
				} else 
					throw new UnsupportedOperationException();
			}
			
		};
		return listener;
	}
	
	private class ColorButton extends JButton implements ColorObserver{
		private ColorChooserFrame frame = null;
		private SettingsEnum type = null;
		private Color color = null;
		
		private ColorButton(SettingsEnum type, Color color){
			control.addColorObserver(this);
			this.type = type;
			this.color = color;
			this.setName(type.toString());
			this.setPreferredSize(new Dimension(10,10));
			this.setBackground(color);
			this.addActionListener(new ActionListener(){
								
				@Override
				public void actionPerformed(ActionEvent ae) {
					if(frame==null)
						frame = new ColorChooserFrame(ColorButton.this.color);
					frame.setVisible(true);
				}
			});
		}
		
		@Override
		public void colorUpdated(SettingsEnum type, Color color) {
			if(this.type==type){
				if(frame!=null) //TODO fix maybe
					frame.cc.setColor(color);
				ColorButton.this.setBackground(color);
				this.color = color;
			}
		}
		
		private class ColorChooserFrame extends JFrame implements ActionListener{
			private JColorChooser cc = null;
			private final String[] NAMES = {"ok","cancel","restore"};
			
			private ColorChooserFrame(Color color){
				cc = createColorChooser(color);
				createFrame();
			}
			
			private void createFrame(){
				this.setResizable(false);
				this.setTitle(type.toString());
				this.setLayout(new BorderLayout());
				this.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);

				this.add(cc, BorderLayout.CENTER);
				
				JPanel optionsPanel = new JPanel();
				JButton[] options = new JButton[3];
				final String[] uiKeys = {"ColorChooser.okText","ColorChooser.cancelText","ColorChooser.resetText"};
				for(int i=0;i<options.length;i++){
					options[i] = new JButton(UIManager.getString(uiKeys[i]));
					options[i].setName(NAMES[i]);
					options[i].addActionListener(this);
					optionsPanel.add(options[i]);
				}
				this.add(optionsPanel, BorderLayout.SOUTH);
				
				this.pack();
			}
			
			private JColorChooser createColorChooser(Color color){
				JColorChooser cc = new JColorChooser(color);
				for(AbstractColorChooserPanel p : cc.getChooserPanels())
					if(!p.getDisplayName().equals("HSB"))
						cc.removeChooserPanel(p);
				return cc;
			}
			
			@Override
			public void actionPerformed(ActionEvent ae) {
				this.setVisible(false);
				String name = ((JButton) ae.getSource()).getName();
				if(name.equals(NAMES[0]))
					control.changeColor(type, cc.getColor());
				else if(name.equals(NAMES[2]))
					control.restoreColor(type);
			}			
		}
	}
}
